#ifndef MODULEHANDLER_H_
#define MODULEHANDLER_H_

#include <pthread.h>
#include <string.h>
#include <sstream>
#include <StatusElement.h>
#include <ControlElement.h>
#include <../ByteString.h>
#include <../WebSocketConnection.h>

using namespace std;
/**
 * \brief Carries necessary data to produce the multiple-condition listening behavior of \a waitForButtonPush()
 */
struct blockingStruct {
	pthread_cond_t* cond; ///<cond is the Control Element's condition
	pthread_mutex_t* lock; ///<lock is the Control Element's lock
	pthread_cond_t* masterCond; ///<masterCond is the condition which signals waitForButtonPush that the server has called buttonPushed
	pthread_mutex_t* masterLock; ///<masterLock is the accompanying lock to masterCond
	unsigned int ID; ///<ID is the Control Element ID identifying specific behavior within a module
	unsigned int* returnID; ///<returnID is the value which will be returned to the module.  It will be updated with the identifier of the first ControlElement clicked.
	bool* masterFlag; ///<masterFlag indicates whether a signal is due to buttonPushed method or general cleanup of the waitForButtonPush method
};

union dataConvert {
	int i;
	long l;
	double d;
	bool b;
	char c[sizeof(double)];
};

/**
 * \class ModuleHandler
 * \brief A class which holds information and facilitates communication
 * between the ProMDATE server and modules.
 *
 * There are two distinct duties performed by the ModuleHandler class: storing
 * data which must be accessed by both the server and module, and providing a
 * means of communication between the server and module.
 *
 * ModuleHandler stores any information the module wishes to share with a user
 * in either a StatusElement or ControlElement class (together referred to as
 * Elements).  The distinction between the two is that StatusElements may only
 * be used to display information.  The end-user may not influence the module in
 * any way through StatusElements.  ControlElements, on the other hand, allow for
 * near-real-time interaction with modules in a thread-safe and theoretically
 * connection unlimited manner.  For information on this, see the main/server
 * documentation.
 *
 *
 * There is a third class capable of holding information and being manipulated by
 * both the server and module -- the Control class.  Controls are analogous to
 * StatusElements in that they simply store a value which is relevant to the module,
 * but this value can be altered by the end-user through the use of HTML forms.
 *
 *
 * StatusElements consist only of a description and a single value. This value can
 * be a string, bool, long, OR double.  Controls also consist only of a description
 * and a signal value or type string, bool, long, OR double.  ControlElements consist
 * of a description, a button name, and a list of Controls.  The button name is
 * applied by the server to a "Send" button associated with the ControlElement.
 *
 * ControlElements allow for the grouping of an infinite number of Controls.  These
 * Controls are updated by the server with the values input into the HTML forms when
 * a ControlElements button is clicked.  The server also signals the button was
 * clicked, which allows the module to perform some action using the updated Control
 * values.
 *
 * ModuleHandler was designed to be thread-safe and passive.  As such, access to all
 * data it contains (Elements and data they contain) is inherently thread-safe and no
 * precautions must be taken by the module.  As a result of this, if modules wish to
 * listen for button pushes outside of their main thread, the methods \a getLock()
 * and \a giveLock() must be called by the start method and secondary threads respectively.
 * This is because of the overlapping lock scheme used by ModuleHandler to ensure no
 * signal given by the server is lost.
 */

class ModuleHandler {
public:
	ModuleHandler(string name);
	virtual ~ModuleHandler();

public:
	unsigned int addStatusElement(int type, string description);
	unsigned int addControlElement(string description, string buttonName = "");
	unsigned int addControl(unsigned int controlElementID, int type,
			string description);
	unsigned int setOrder(int type, unsigned int* IDs, int* order, int length);

	unsigned int remove(unsigned int ID);

	void setControlElementButtonName(unsigned int controlElementID,
			string newButtonName);

	string getControlElementButtonName(unsigned int controlElementID);
	int getNumberControlsInControlElement(unsigned int controlElementID);

	int getElementType(unsigned int identifier);
	string getDescription(unsigned int identifier);

	unsigned int waitForButtonPush();
	void abandonButtonWait();
	string buttonPushed(unsigned int controlElementIdentifier,
			string& newResponseFilePath);
	void giveResponse(string newResponse, string newReponseFilePath);

	string getModuleName();
	string getModuleDescription();
	void setModuleDescription(string newModuleDescription);

	void giveLock();
	void getLock();

	void setValue(unsigned int ID, void* newValue);

	string getValueString(unsigned int ID);
	long getValueInt(unsigned int ID);
	double getValueDouble(unsigned int ID);
	bool getValueBool(unsigned int ID);

	unsigned int errsig;
	unsigned int buttonWaitErr;

	enum buttonErrEnum {
		NOCONELS = 1, ABANDONED = 2
	};

private:
	string moduleName;
	string moduleDescription;
	pthread_rwlock_t moduleLock;

	int numS;
	StatusElement** sList;
	unsigned int** sOrder;
	pthread_rwlock_t sLock;
	int numC;
	ControlElement** cList;
	unsigned int** cOrder;
	pthread_rwlock_t cLock;
	int idOverflow;
	unsigned int conBits;

	pthread_mutex_t responseLock;
	pthread_cond_t responseCond;
	string response;
	string responseFilePath;

	unsigned int* IDs;
	int IDsLength;
	pthread_mutex_t IDsLock;

	static void* buttonBroadcastListener(void* blockingStructVoid);
	pthread_mutex_t* masterLock;
	pthread_cond_t* masterCond;

	pthread_rwlock_t transferLock;
	pthread_mutex_t getGiveLock;
	pthread_cond_t getGiveCond;

	void updateIDsList();

	void sendStatusElementBS(int orderindex);
	void sendControlElementBS(int orderindex);
};

#endif /* MODULEHANDLER_H_ */
